import json
import uvicorn
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Query
from fastapi.middleware.cors import CORSMiddleware
from typing import List, Dict, Optional
import asyncio
from contextlib import asynccontextmanager

from models.models import UserRealName, User, UserOrder
from tools.bdapi import idcard
from tools.myredis import r
from views.coze1 import coze_api
from views.orders import orders_router
from views.rabbitmq1 import rebbit_result
from views.sys import sys
from views.user import user_router
from views.hotel import hotel_router
from tortoise.contrib.fastapi import register_tortoise
from config.config_dev import TORTOISE_ORM
from apscheduler.schedulers.asyncio import AsyncIOScheduler

# 创建调度器实例
scheduler = AsyncIOScheduler()

# 定义 lifespan 事件处理器
# 开启调度器


# 创建FastAPI应用实例，使用 lifespan 替代 on_event
app = FastAPI()

# 注册Tortoise-ORM
register_tortoise(
    app,
    config=TORTOISE_ORM,
    generate_schemas=False,  # 不自动创建表，使用我们单独的迁移脚本
    add_exception_handlers=True,
)

# 定义允许跨域请求的来源列表
origins = [
    "http://localhost",
    "http://localhost:5173",
    "http://localhost:5174",
    "http://localhost:8000",
    # 可以添加更多允许的域名，如生产环境域名
    "https://yourproductiondomain.com"
]

# 添加 CORS 中间件
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,  # 允许的来源列表
    allow_credentials=True,  # 允许携带凭证（如 cookies）
    allow_methods=["*"],  # 允许所有HTTP方法
    allow_headers=["*"],  # 允许所有HTTP请求头
)

# 注册路由
app.include_router(user_router, prefix="/user")
app.include_router(coze_api, prefix="/coze")
# 根路径
app.include_router(sys,prefix='/sys')
app.include_router(orders_router,prefix = '/orders')
app.include_router(hotel_router)
app.include_router(rebbit_result,prefix='/rebbit')
@app.get("/")
async def root():
    return {"message": "欢迎使用MyProject API"}

# 健康检查端点
@app.get("/health")
async def health_check():
    return {"status": "healthy"}

# 获取事件循环
try:
    loop = asyncio.get_running_loop()
except RuntimeError:
    loop = asyncio.new_event_loop()

# 存储所有活跃的 WebSocket 连接
active_connections: List[WebSocket] = []
# 存储客户端ID与连接的映射（可选，用于定向推送）
client_connections: Dict[str, WebSocket] = {}


# 广播消息给所有连接的客户端
async def broadcast(message: str):
    for connection in active_connections:
        await connection.send_text(message)


# 定向推送消息给指定客户端
async def send_to_client(client_id: str, message: str):
    if client_id in client_connections:
        await client_connections[client_id].send_text(message)

user_dict={}
service_dict={}
service_id='1001'
@app.websocket("/ws/{client_id}")
async def websocket_endpoint(websocket: WebSocket, client_id: str, client_name: Optional[str] = Query(None)):
    # 接受客户端连接
    await websocket.accept()
    # 存储连接
    active_connections.append(websocket)
    client_connections[client_id] = websocket
    print(f"客户端 {client_id} 已连接，当前连接数：{len(active_connections)}")
    if client_id.find('service') == -1:
        print('用户连接')
        # 用户连接的 key: 用户ID + 客服ID (如: '302b29...' + '1001')
        key = client_id + service_id
        user_dict[key] = websocket
        print(f"用户连接 key: {key}")
        print(f"用户ID: {client_id}, 客服ID: {service_id}")
        print(f"当前用户连接数: {len(user_dict)}, 连接keys: {list(user_dict.keys())}")
        # 若服务端已连接，向服务端发送用户信息
        if service_id in service_dict:
            await service_dict[service_id].send_text(json.dumps({"userid": client_id, 'name': "张三"}))
            print(f"已通知客服端 {service_id}：用户 {client_id} 已连接")
        else:
            print(f"警告: 服务端 {service_id} 尚未连接，无法发送用户上线通知")
            print(f"当前已连接的客服: {list(service_dict.keys())}")
    else:
        # 客服连接：直接使用 service_id 作为 key
        service_dict[service_id] = websocket
        print(f'客服连接: service_id = {service_id}')
        print(f"当前客服连接数: {len(service_dict)}, 连接keys: {list(service_dict.keys())}")
        # 通知所有已连接的用户，客服已上线（可选）
        if user_dict:
            print(f"客服上线，当前有 {len(user_dict)} 个用户连接")

    try:
        while True:
            # 接收客户端发送的消息
            data = await websocket.receive_text()
            data = json.loads(data)
            print(data)
            # 用户
            # to客服
            fromuser = data['from']
            touser = data['to']
            # key = fromuser + touser
            print(f"从: {fromuser}, 到: {touser}")

            # 初始化一个变量 target_websocket 为 None，用于存储目标客户端的 WebSocket 连接。
            target_websocket = None
            lookup_key = None
            
            # 判断消息的目标用户（touser）是否以 'service' 开头，即是否是发给客服的消息
            if touser.startswith('service'):
                # 如果是发给客服的消息，移除 'service' 前缀，只保留数字部分（如 'service1001' -> '1001'）。
                service_id_clean = touser.replace('service', '')
                # 在 service_dict 字典中查找对应客服端ID的 WebSocket 连接如果找到则赋值给 target_websocket。
                target_websocket = service_dict.get(service_id_clean)
                lookup_key = f"service_dict[{service_id_clean}]"
                print(f"查找客服连接: service_dict[{service_id_clean}], 是否存在: {service_id_clean in service_dict}")
                print(f"当前所有客服连接: {list(service_dict.keys())}")
            else:
                # 判断消息的发送方（fromuser）是否是客服
                if fromuser.startswith('service'):
                    # 提取客服ID中的纯数字部分（如 'service1001' -> '1001'）。
                    service_id_clean = fromuser.replace('service', '')
                    # 构建用户连接的键，格式为：用户ID + 客服ID（如 '302b29...' + '1001' = '302b29...1001'）。
                    key = touser + service_id_clean
                    lookup_key = f"user_dict[{key}]"
                    # 在 user_dict 字典中查找对应用户连接的 WebSocket 连接
                    target_websocket = user_dict.get(key)
                    print(f"查找用户连接: user_dict[{key}], 是否存在: {key in user_dict}")
                    print(f"当前所有用户连接: {list(user_dict.keys())}")

            if target_websocket:
                # 通过找到的 WebSocket 连接向目标客户端发送消息
                await target_websocket.send_text(json.dumps(data))
                print(f"消息已成功发送到 {lookup_key}")
            else:
                print(f"目标连接不存在: {lookup_key or '未知'}")
                print(f"消息详情 - 从: {fromuser}, 到: {data.get('to')}")
                print(f"当前连接的客服: {list(service_dict.keys())}")
                print(f"当前连接的用户数量: {len(user_dict)}")

            print(f"收到客户端 {client_id} 的消息：{data}")

            # 2. 广播给所有客户端（可选）
            # await broadcast(f"客户端 {client_id} 说：{data}")

            # 3. 模拟服务器主动推送（5秒后推送一条消息）
            # await asyncio.sleep(5)
            # await websocket.send_text(f"服务器主动推送：你5秒前发送了「{data}」")

    except WebSocketDisconnect:
        # 客户端断开连时清理连接
        active_connections.remove(websocket)
        client_connections.pop(client_id, None)
        print(f"客户端 {client_id} 已断开，当前连接数：{len(active_connections)}")


# 提供一个HTTP接口，用于外部触发服务器推送（可选）
@app.post("/push/{client_id}")
async def push_to_client(client_id: str):
    if client_id in client_connections:
        await send_to_client(client_id, f"管理员推送")
        return {"status": "success", "message": f"已向 {client_id} 推送消息"}
    return {"status": "error", "message": f"客户端 {client_id} 未连接"}


async def idcard_ocr_job():
    key = "idcard_list"
    global client_connections, loop
    if r.llen(key) == 0:
        return {"code": 200, "msg": "身份证识别队列为空"}
    user_list = r.lrange(key, 0, 10)
    user_list = r.lrange(key, 0, 9)
    for i in user_list:
        # 处理bytes类型，转为字符串
        if isinstance(i, bytes):
            i_str = i.decode('utf-8')
        else:
            i_str = i

        try:
            data = json.loads(i_str)
            user_img = data.get("url")
            user_id = data.get("user_id")

            if not user_img:
                print(f"队列数据缺少url字段，跳过: {data}")
                # 删除无效数据
                r.lrem(key, 0, i)
                continue

            # 执行身份证识别
            print(f"开始识别身份证图片: {user_img}, 用户ID: {user_id}")
            ocr_result = idcard.idcard(user_img)

            # 将识别结果保存到Redis（以图片user_id为key）
            r.set_str(user_id, json.dumps(ocr_result))
            print(f"身份证识别完成: {user_img}")
            # 将redis中的数据同步到数据库，让后删除redis中的数据
            card_result = json.loads(r.get_str(user_id))
            words_result = card_result.get("words_result", {})

            # 正确提取身份证信息
            realname_info = {
                "name": words_result.get("姓名", {}).get("words", ""),  # 姓名
                "id_num": words_result.get("公民身份号码", {}).get("words", ""),  # 身份证号码
                "sex": words_result.get("性别", {}).get("words", ""),  # 性别
                "nation": words_result.get("民族", {}).get("words", ""),  # 民族
                "birth": words_result.get("出生", {}).get("words", ""),  # 出生日期
                "address": words_result.get("住址", {}).get("words", "")  # 地址
            }

            # 处理出生日期格式，确保它可以被正确解析为日期对象
            birth_date_str = realname_info["birth"]
            if not birth_date_str:
                # 如果没有出生日期，则设置为默认日期或None
                birth_date = None
            else:
                # 确保日期格式正确，例如："20030623" -> "2003-06-23"
                try:
                    # 直接使用原始字符串，假设格式是 YYYYMMDD
                    year = birth_date_str[:4]
                    month = birth_date_str[4:6]
                    day = birth_date_str[6:8]
                    formatted_birth_date = f"{year}-{month}-{day}"
                    birth_date = formatted_birth_date
                except Exception:
                    # 如果无法正确解析日期，设置为None
                    birth_date = None

            # 创建数据库记录，注意处理日期字段
            await UserRealName.create(
                user_id=user_id,
                name=realname_info["name"],
                sex=realname_info["sex"],
                nation=realname_info["nation"],
                birthday=birth_date,  # 使用处理后的日期
                address=realname_info["address"],
                id_card=realname_info["id_num"],
                id_card_front=user_img
            )

            # 将user表中的verify_status设置为2，表示已通过身份证验证
            await User.filter(id=user_id).update(verify_status=2)
            print(f"身份证信息保存到数据库: {user_img}, 用户ID: {user_id}")
            # 保存成功，删除redis中的数据
            r.delete(user_id)
            print(f"身份证信息已从Redis删除: {user_img}, 用户ID: {user_id}")
            # 向用户推送识别完成消息
            if user_id and user_id in client_connections:
                try:
                    await client_connections[user_id].send_text(
                        json.dumps({
                            "type": "ocr_complete",
                            "url": user_img,
                            "result": ocr_result
                        })
                    )
                except Exception as ws_error:
                    print(f"推送WebSocket消息失败: {ws_error}")

            # 识别成功后，从队列中删除
            deleted = r.lrem(key, 0, i)
            print(f"已从队列删除: {user_img}, 删除数量: {deleted}")

        except Exception as e:
            print(f"身份证识别失败: {str(e)}")
            # 识别失败时，可以选择删除或保留在队列中
            # 如果需要保留以便重试，可以注释掉下面这行
            # r.lrem(key, 0, i)

            # 通知用户识别失败
            if user_id and user_id in client_connections:
                try:
                    await client_connections[user_id].send_text(
                        json.dumps({
                            "type": "ocr_failed",
                            "url": user_img,
                            "error": str(e)
                        })
                    )
                except Exception as ws_error:
                    print(f"推送失败消息失败: {ws_error}")


async def cancel_expired_orders():
    """
    定时任务：取消超过15分钟仍未支付的订单
    """
    try:
        from datetime import datetime, timedelta
        from tortoise.exceptions import DoesNotExist
        
        # 计算15分钟前的时间
        cutoff_time = datetime.now() - timedelta(minutes=15)
        
        # 查找15分钟前创建且状态为待支付的订单
        expired_orders = await UserOrder.filter(
            status=0,  # 0代表待支付状态
            create_time__lte=cutoff_time
        ).all()
        
        cancelled_count = 0
        for order in expired_orders:
            # 将订单状态更新为已取消（状态码2）
            order.status = 2
            await order.save()
            cancelled_count += 1
            
        if cancelled_count > 0:
            print(f"已取消 {cancelled_count} 个超时未支付的订单")
            
    except Exception as e:
        print(f"执行取消超时订单任务时出错: {e}")


def add_jobs():
    # 任务1：每隔3秒执行一次身份证识别任务
    # 检查是否已存在同名任务，避免冲突
    if not scheduler.get_job("idcard_ocr_job"):
        scheduler.add_job(
            func=idcard_ocr_job,
            trigger="interval",
            seconds=3,
            id="idcard_ocr_job",
        )
    
    # 任务2：每分钟检查一次超时未支付订单并取消
    if not scheduler.get_job("cancel_expired_orders"):
        scheduler.add_job(
            func=cancel_expired_orders,
            trigger="interval",
            minutes=1,
            id="cancel_expired_orders",
        )


# @app.on_event("startup")
# async def startup_event():
#     add_jobs()
#     # 用于检查 APScheduler 调度器是否处于未运行状态
#     if not scheduler.running:
#         scheduler.start()
#         print("调度器已启动")


# 关闭调度器
@app.on_event("shutdown")
def shutdown_event():
    scheduler.shutdown()
    print("调度器已关闭")

if __name__ == '__main__':
    uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)


